package com.billionsfinance.hessian.core;


import com.billionsfinance.hessian.db.ConfigDataSource;
import com.billionsfinance.hessian.mapper.ClientInfoMapper;
import com.billionsfinance.hessian.mapper.ServerInfoMapper;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;

/**
 * @fileName: HessianInitFactory.java
 * @author: WeiHui.Zhang
 * @date: 2016-09-28  9:48
 * @version: v1.0.0
 */
public abstract class HessianInitFactory {

	Logger LOGGER = LoggerFactory.getLogger(getClass());

	public static final String DEFAULT_MYBATIS_MAPPING_FILE = "com/billionsfinance/hessian/mapping/mappings.xml";

	public static final String DEFAULT_CREATE_BY = "system";

	protected String appCode;

	protected String createBy = DEFAULT_CREATE_BY;
	/**
	 * 数据源
	 */
	protected DataSource dataSource;

	/**
	 * Hessian接口包
	 */
	protected String[] servicePackages;

	/**
	 * Hessian接口实现包
	 */
	protected String[] serviceImplPackages;

	/**
	 * 自动更新表[Drop and re-create]
	 */
	protected boolean tableSchemaUpdate = false;

	/**
	 * 自动创建表
	 */
	protected boolean tableSchemaCreate = true;

	/**
	 * 自动插入hessian服务信息
	 */
	protected boolean autoInsert = true;

	/**
	 * 数据库类型
	 */
	protected String databaseType;

	/**
	 * 自定义mapper.java
	 */
	protected Set<Class<?>> customMybatisMappers;

	/**
	 * 自定义mapper.xml
	 */
	protected Set<String> customMybatisXMLMappers;

	/**
	 * sqlSessionFactory
	 */
	protected SqlSessionFactory sqlSessionFactory;

	public void setAppCode(String appCode) {
		this.appCode = appCode;
	}

	public void setCreateBy(String createBy) {
		this.createBy = createBy;
	}

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public void setServicePackages(String[] servicePackages) {
		this.servicePackages = servicePackages;
	}

	public void setServiceImplPackages(String[] serviceImplPackages) {
		this.serviceImplPackages = serviceImplPackages;
	}

	public void setTableSchemaUpdate(boolean tableSchemaUpdate) {
		this.tableSchemaUpdate = tableSchemaUpdate;
	}

	public void setTableSchemaCreate(boolean tableSchemaCreate) {
		this.tableSchemaCreate = tableSchemaCreate;
	}

	public void setDatabaseType(String databaseType) {
		this.databaseType = databaseType;
	}

	public void setAutoInsert(boolean autoInsert) {
		this.autoInsert = autoInsert;
	}

	/**
	 * 初始化mybatis配置信息
	 */
	protected void initSqlSessionFactory() {
		TransactionFactory transactionFactory = new ManagedTransactionFactory();

		Environment environment = new Environment("development", transactionFactory, dataSource);

		Reader reader = new InputStreamReader(getMyBatisXmlConfigurationSteam());

		Configuration configuration = initMybatisConfiguration(environment, reader);

		sqlSessionFactory = new DefaultSqlSessionFactory(configuration);
	}

	protected Configuration initMybatisConfiguration(Environment environment, Reader reader) {
		XMLConfigBuilder parser = new XMLConfigBuilder(reader, "");
		Configuration configuration = parser.getConfiguration();
		configuration.setEnvironment(environment);

		initCustomMybatisMappers(configuration);

		configuration = parseMybatisConfiguration(parser);
		return configuration;
	}

	protected Configuration parseMybatisConfiguration(XMLConfigBuilder parser) {
		return parseCustomMybatisXMLMappers(parser.parse());
	}

	protected Configuration parseCustomMybatisXMLMappers(Configuration configuration) {
		if (getCustomMybatisXMLMappers() != null)
			// see XMLConfigBuilder.mapperElement()
			for (String resource : getCustomMybatisXMLMappers()) {
				XMLMapperBuilder mapperParser = new XMLMapperBuilder(getResourceAsStream(resource),
						configuration, resource, configuration.getSqlFragments());
				mapperParser.parse();
			}
		return configuration;
	}

	protected InputStream getMyBatisXmlConfigurationSteam() {
		return getResourceAsStream(DEFAULT_MYBATIS_MAPPING_FILE);
	}

	protected InputStream getResourceAsStream(String resource) {
		InputStream resourceStream = null;
		ClassLoader classLoader = HessianServerInitFactory.class.getClassLoader();
		if (classLoader != null) {
			resourceStream = classLoader.getResourceAsStream(resource);
		}
		if (resourceStream == null) {
			// Try the current Thread context classloader
			classLoader = Thread.currentThread().getContextClassLoader();
			resourceStream = classLoader.getResourceAsStream(resource);
		}
		return resourceStream;
	}

	protected void initCustomMybatisMappers(Configuration configuration) {
		if (getCustomMybatisMappers() != null) {
			for (Class<?> clazz : getCustomMybatisMappers()) {
				configuration.addMapper(clazz);
			}
		}
	}

	public Set<Class<?>> getCustomMybatisMappers() {
		return customMybatisMappers;
	}

	public Set<String> getCustomMybatisXMLMappers() {
		return customMybatisXMLMappers;
	}

	protected void init() {
		LOGGER.info("开始初始化Hessian配置信息");
		StopWatch watch = new StopWatch();
		watch.start();
		initData();
		initSqlSessionFactory();
		initHessianData();
		watch.stop();
		LOGGER.info("初始化Hessian配置信息成功,耗时{}毫秒", watch.getTime());
	}

	/**
	 * 初始化数据
	 *
	 * @return
	 */
	protected void initData() {
		customMybatisMappers = new HashSet<>();
		customMybatisMappers.add(ServerInfoMapper.class);
		customMybatisMappers.add(ClientInfoMapper.class);
	}

	/**
	 * 初始化Hessian数据
	 *
	 * @return
	 */
	protected abstract void initHessianData();

	protected void doUpdate(Connection connection, ConfigDataSource databaseType) {
		doDrop(connection, databaseType);
		doCreate(connection, databaseType);
	}

	protected void doDrop(Connection connection, ConfigDataSource databaseType) {
		sqlExecutor(connection, databaseType.getServerDropSql());
	}

	protected void doCreate(Connection connection, ConfigDataSource databaseType) {
		sqlExecutor(connection, databaseType.getServerCreateSql());
	}

	protected void sqlExecutor(Connection connection, String sql) {
		PreparedStatement statement = null;
		try {
			statement = connection.prepareStatement(sql);
			statement.execute();
		} catch (SQLException e) {
			throw new RuntimeException("执行SQL错误", e);
		} finally {
			if (null != statement) {
				try {
					statement.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}

	protected void close(ResultSet rs, PreparedStatement statement, Connection connection) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if (statement != null) {
			try {
				statement.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if (connection != null) {
			try {
				connection.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}